﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace FierceWar
{
	public class BoundingBoxTree
	{
		public BoundingBoxNode Root;
		public List<BoundingBoxNode> Leave;
		private static int id;
		private List<BoundingBoxNode> _locations = new List<BoundingBoxNode>();

		public BoundingBoxTree(Vector3 min, Vector3 max)
		{
			Root = new BoundingBoxNode(min, max);
			Leave = new List<BoundingBoxNode>();
			id = 0;
			Build(Root);
		}

		public void Insert(ElementalModel model)
		{
			Insert(Root, model);
		}

		public void InsertForBuildingMap(ElementalModel model)
		{
			InsertForBuildingMap(Root, model);
		}

		public void Remove(ElementalModel model)
		{
			foreach (int index in model.BoundingBoxMasterIndexes)
			{
				Leave[index].Models.Remove(model);
			}
			model.BoundingBoxMasterIndexes.Clear();
		}

		public void FindLocation(ElementalModel model)
		{
			FindLocation(Root, model);
		}

		public void FindLocationForBuildingMap(ElementalModel model)
		{
			FindLocationForBuildingMap(Root, model);
		}

		public bool HasCollision(ElementalModel model)
		{
			return HasCollision(Root, model);
		}

		public bool HasCollisionForBuildingMap(ElementalModel model)
		{
			return HasCollisionForBuildingMap(Root, model);
		}

        public ElementalModel IsShooted(ElementalModel model)
        {
            _locations.Clear();
            FindLocation(Root, model);
            foreach (BoundingBoxNode bbn in _locations)
            {
                for (int i = 0; i < bbn.Models.Count; i++)
                {
                    if ((bbn.Models[i] != model) &&
                         bbn.Models[i].OrientedBoundingBox.Intersects(model.OrientedBoundingBox))
                    {
                        return bbn.Models[i];
                    }
                }
            }

            return null;
        }

		#region Private methods.
		private void Build(BoundingBoxNode root)
		{
			Vector3[] min = new Vector3[8];
			Vector3[] max = new Vector3[8];
			min[0] = root.OrientedBoundingBox.Min;
			max[7] = root.OrientedBoundingBox.Max;
			float width = (max[7].X - min[0].X) / 2;
			float height = (max[7].Y - min[0].Y) / 2;
			float depth = (max[7].Z - min[0].Z) / 2;

			min[1] = new Vector3(min[0].X, min[0].Y, min[0].Z + depth);
			min[2] = new Vector3(min[0].X + width, min[0].Y, min[0].Z);
			min[3] = new Vector3(min[2].X, min[2].Y, min[2].Z + depth);
			min[4] = new Vector3(min[0].X, min[0].Y + height, min[0].Z);
			min[5] = new Vector3(min[4].X, min[4].Y, min[4].Z + depth);
			min[6] = new Vector3(min[4].X + width, min[4].Y, min[4].Z);
			min[7] = new Vector3(min[6].X, min[6].Y, min[6].Z + depth);

			max[6] = new Vector3(max[7].X, max[7].Y, max[7].Z - depth);
			max[5] = new Vector3(max[7].X - width, max[7].Y, max[7].Z);
			max[4] = new Vector3(max[5].X, max[5].Y, max[5].Z - depth);
			max[3] = new Vector3(max[7].X, max[7].Y - height, max[7].Z);
			max[2] = new Vector3(max[3].X, max[3].Y, max[3].Z - depth);
			max[1] = new Vector3(max[3].X - width, max[3].Y, max[3].Z);
			max[0] = new Vector3(max[1].X, max[1].Y, max[1].Z - depth);

			for (int i = 0; i < 8; i++)
			{
				BoundingBoxNode bbn = new BoundingBoxNode(min[i], max[i]);
				root.Children.Add(bbn);
				if (width > 4096)
				{
					Build(bbn);
				}
				else
				{
					bbn.Identifier = id++;
					Leave.Add(bbn);
				}
			}
		}

		private void Insert(BoundingBoxNode root, ElementalModel model)
		{
			foreach (BoundingBoxNode bbn in root.Children)
			{
				if (model.OrientedBoundingBox.Intersects(bbn.OrientedBoundingBox))
				{
					if (bbn.IsLeaf)
					{
						if (!bbn.Models.Contains(model))
						{
							bbn.Models.Add(model);
							model.BoundingBoxMasterIndexes.Add(bbn.Identifier);
						}
					}
					else
					{
						foreach (BoundingBoxNode bbnc in bbn.Children)
						{
							Insert(bbnc, model);
						}
					}
				}
			}
		}

		private void InsertForBuildingMap(BoundingBoxNode root, ElementalModel model)
		{
			for (int i = 0; i < 4; i++)
			{
				if (model.OrientedBoundingBox.Intersects(root.Children[i].OrientedBoundingBox))
				{
					if (root.Children[i].IsLeaf)
					{
						if (!root.Children[i].Models.Contains(model))
						{
							root.Children[i].Models.Add(model);
							model.BoundingBoxMasterIndexes.
								Add(root.Children[i].Identifier);
						}
					}
					else
					{
						for (int j = 0; j < 4; j++)
						{
							InsertForBuildingMap(root.Children[j], model);
						}
					}
				}
			}
		}

		private bool HasCollision(BoundingBoxNode root, ElementalModel model)
		{
			_locations.Clear();
			FindLocation(root, model);
			foreach (BoundingBoxNode bbn in _locations)
			{
				foreach (ElementalModel em in bbn.Models)
				{
					if ((em != model) &&
						em.OrientedBoundingBox.Intersects(model.OrientedBoundingBox))
					{
						return true;
					}
				}
			}

			return false;
		}

		private bool HasCollisionForBuildingMap(BoundingBoxNode root, ElementalModel model)
		{
			_locations.Clear();
			FindLocationForBuildingMap(root, model);
			foreach (BoundingBoxNode bbn in _locations)
			{
				foreach (ElementalModel em in bbn.Models)
				{
					if ((em != model) &&
						em.OrientedBoundingBox.Intersects(model.OrientedBoundingBox))
					{
						return true;
					}
				}
			}

			return false;
		}

		private void FindLocation(BoundingBoxNode root, ElementalModel model)
		{
			foreach (BoundingBoxNode bbn in root.Children)
			{
				if (model.OrientedBoundingBox.Intersects(bbn.OrientedBoundingBox))
				{
					if (bbn.IsLeaf)
					{
						if (!_locations.Contains(bbn))
						{
							_locations.Add(bbn); 
						}
					}
					else
					{
						foreach (BoundingBoxNode bbnc in bbn.Children)
						{
							FindLocation(bbnc, model);
						}
					}
				}
			}
		}

		private void FindLocationForBuildingMap(BoundingBoxNode root, ElementalModel model)
		{
			for (int i = 0; i < 4; i++)
			{
				if (model.OrientedBoundingBox.Intersects(root.Children[i].OrientedBoundingBox))
				{
					if (root.Children[i].IsLeaf)
					{
						if (!_locations.Contains(root.Children[i]))
						{
							_locations.Add(root.Children[i]); 
						}
					}
					else
					{
						for (int j = 0; j < 4; j++)
						{
							FindLocationForBuildingMap(root.Children[j], model);
						}
					}
				}
			}
		}
		#endregion
	}
}
